Опубликован: 26.09.2006 | Доступ: свободный | Студентов: 1803 / 490 | Оценка: 4.25 / 4.12 | Длительность: 17:09:00
ISBN: 978-5-9556-0066-6
Специальности: Программист, Математик
Лекция 9:

Толстые кучи

Процедура заключается в выполнении операторов

\formula{
\t if\ (p1\t{\^{}}.{\rm key} \le
p2\t{\^{}}.{\rm Key})\
\t{and}\ (p1\t{\^{}}.{\rm key} \le p3\t{\^{}}.{\rm Key})\ \t then \\
\{{\rm MinP} := p1;\ p1:=p2;\ p2:=p3\};\\
\t if\ (p2\t{\^{}}.{\rm key} \le
p1\t{\^{}}.{\rm Key})\ \t{and}\
(p2\t{\^{}}.{\rm key} \le p3\t{\^{}}.{\rm Key})\ \t then\\
\{{\rm MinP} := p2;\ p1:= p1;\ p2:= p3\};\\
\t if\ (p3\t{\^{}}.{\rm key} \le
p1\t{\^{}}.{\rm Key})\ \t{and}\
(p3\t{\^{}}.{\rm key} \le p2\t{\^{}}.{\rm Key})\ \t then\\
\mbox{}\q \{{\rm MinP}:= p3;\ p1:= p1;\ p2:= p2\};\\
\mbox{}\q p1\t{\^{}}.{\rm Right} := p2;\ p1\t{\^{}}.{\rm Left} := {\rm nil};\
p1\t{\^{}}.{\rm Parent} := {\rm MinP};\\
\mbox{}\q p2\t{\^{}}.{\rm Right} := {\rm MinP}\t{\^{}}.{\rm LChaild};\
p2\t{\^{}}.{\rm Left} := p1;\ p2\t{\^{}}.{\rm Parent} := {\rm MipP};\\
\mbox{}\q \t{if}\ ({\rm PMin}\t{\^{}}.{\rm LChild} \ne {\rm NiL})\ \t{then}\
{\rm PMin}\t{\^{}}.{\rm LChild}\t{\^{}}.{\rm Left} :=p2;\\
{\rm MinP}\t{\^{}}.{\rm LChaild} := p1;\ {\rm MinP}\t{\^{}}.{\rm Rank}
:= {\rm MinP}\t{\^{}}.{\rm Rank} +1;\\
{\rm PMin}\t{\^{}}.{\rm Right} := {\rm NiL};\ {\rm PMin}\t{\^{}}.{\rm Left}
:={\rm NiL};\ {\rm Fastening}:= {\rm MinP};
}

Функция GetKey (p) по указателю p на элемент определяет значение его ключа и реализуется оператором

\formula{
\t if\ (p = {\rm nil})\ \t then\ {\rm Min} := \infty\
\t else\ {\rm Min} := p\t{\^{}}.{\rm
Key};\ {\rm GetKey} :=
{\rm Min};
}

Функция MinKeyNodeRoot(p), которая по указателю p на списочную часть разряда корневого счетчика возвращает указатель на корневой узел с минимальным ключом, реализуется операторами

\formula{
p1:= p;\ {\rm MinP} := p1;\\
\t while\ (p1 \ne {\rm nil})\
\t do\\
\t begin if\ (p1\t{\^{}}.{\rm
Key} < {\rm MinP}\t{\^{}}.{\rm Key})\
\t then\ {\rm MinP} := p1;\ p1:=
p1\t{\^{}}.{\rm Right}\,{\rm End}\\
{\rm MinKeyNodeRoot} := {\rm MinP};
}

Очевидно, что трудоемкость всех приведенных выше операций оценивается величиной O(1).

Операция фиксации ({\rm FixRootCount(i)}) Операция фиксации i -го разряда корневого счетчика подразумевает, что его значение равно трем, а списочная часть содержит указатель на список деревьев ранга i, состоящий ровно из трех деревьев. При выполнении этой операции значение в i -м разряде — должно стать равным нулю, а значение в (i + 1) -м разряде увеличиться на единицу. То есть в куче не должно остаться деревьев ранга i, а количество деревьев ранга i + 1 должно увеличиться на единицу. Для этого следует удалить из кучи три присутствующих в ней дерева ранга i, связать их в дерево ранга i + 1 и вставить вновь полученное дерево в кучу.

Следует учесть, что ранг нового дерева может стать больше, чем {\rm
MaxRank}, что потребует инициализации нового разряда. Для этого необходимо увеличить значение {\rm MaxRank} на единицу и заполнить новое поле, а также провести инициализацию нового разряда.

Операция фиксации осуществляется с помощью операторов

\formula{
\t if\ ({\rm MaxRank} = i)\ \t then\ \{{\rm MaxRank}:= i+1;\
{\rm RootCount}[i+1]\t{\^{}}.{\rm Value}:= 0;\\
{\rm CountViolation}[i+1].{\rm Value}:= 0\}\\
\mbox{}\q \t else\ \{{\rm
UpdateForwardPointer}(i+1)\};\\
{\rm RootCount}[i].{\rm Value}:= 0;\\
p1:= {\rm RootCount}[i].{\rm ListPointer};\ p2:= p1\t{\^{}}.{\rm Right};\
p3:= p2\t{\^{}}.{\rm Right};\\
p:= {\rm Fastening}(p1, p2, p3);\ {\rm RootCount}[i]\t{\^{}}.
{\rm ListPointer}:= {\rm nil};\\
{\rm InsertTree}(i+1, p); \\
{\rm RootCount}[i+1].{\rm Value}:= {\rm RootCount}[i+1].{\rm Value} + 1;
}

Очевидно, что если списочная часть корневого счетчика до операции соответствовала избыточному корневому представлению, то и после операции фиксации это соответствие сохранится. Сохраняется также и регулярность представления. Трудоемкость данной операции O(1).

Инкрементирование i-го разряда корневого счетчика ({\rm IncRootCount(i,p)}). По сравнению с описанным алгоритмом инкрементирования i -го разряда избыточного представления здесь мы должны учесть работу со списочной частью и обновить прямые указатели. Процедура реализуется операторами

\formula{
\t if\ ({\rm RootCount}[i].{\rm Value}
= 1)\ \t{or}\
({\rm RootCount}[i].{\rm Value} = 2)\\
\mbox{}\q \t then if\
({\rm RootCount}[{\rm RootCount}[i].{\rm ForwardPointer}].{\rm Value} =3)\\
\mbox{}\q\qq \t then\ {\rm
FixRootCount}
({\rm RootCount}[i].{\rm ForwardPointer});\\
\t if\ ({\rm RootCount}[i].{\rm Value}
= 3)\
\t then\ {\rm FixRootCount}(i);\\
{\rm InsertTree}(i,p);\\
{\rm RootCount}[i].{\rm Value}:= {\rm RootCount}[i].{\rm Value} + 1;\\
{\rm UpdateForwardPointer}(I);\\
\t if\ ({\rm RootCount}[i].{\rm Value}
= 3)\ \t then\
{\rm FixRootCount}(i);
}

Очевидно, что, если корневой счетчик находится в корректном состоянии и i \le {\rm MaxRank}, то операция инкрементирования i -го разряда корневого счетчика переводит корневой счетчик в новое корректное состояние. Трудоемкость этой операции равна O(1).

Процедура удаления дерева из кучи подразумевает наличие в куче этого дерева. Пусть удаляемое дерево имеет ранг i. Тогда значение i -го разряда избыточного корневого представления не равно нулю. То есть уменьшение этого значения на единицу не испортит регулярности представления и не потребует обновления каких-либо указателей. Необходимо лишь соответствующим образом обработать списочную часть. Процедура реализуется операторами

\formula{
{\rm DeleteTree}(i, p);\ {\rm RootCount}[i].{\rm Value}:=
{\rm RootCount}[i].{\rm Value} -1;
}

Трудоемкость операции O(1).

Нахождение дерева с минимальным ключом в корне ({\rm MinKey}) реализуется операторами

\formula{
{\rm MinP} := {\rm nil};\\
\t for\ i:= 0\ \t to\ {\rm MaxRank}\ \t do \\
\t begin\\
p1 := {\rm MinKeyNodeRoot}\ ({\rm RootCount}[i].{\rm ListPointer});\\
\t if\ ({\rm GetKey}(p1) < {\rm
GetKey}({\rm MinP}))\
\t then\ {\rm MinP}:= p1;\\
\t end;
{\rm MinKey} := {\rm MinP};
}

Трудоемкость данной операции также O(1).

Счетчик нарушений. К сожалению, здесь не удается разделить работу с избыточным представлением и списочной частью, как в корневом счетчике. Поэтому рассмотрим работу со счетчиком нарушений более подробно. Счетчик нарушений состоит из расширенного избыточного двоичного представления и набора списочных элементов.

Отличие заключается в том, что:

  1. Нас теперь интересует не само число, а только значения разрядов.
  2. Операция фиксации тесно связана с толстой кучей.

Значение i -го разряда для счетчика нарушений интерпретируется как количество неправильных узлов ранга i, а его списочная часть — это указатели на неправильные узлы ранга i.

Такое определение счетчика нарушений дает возможность сделать несколько утверждений:

  • Наличие счетчика нарушений позволяет иметь доступ к любому неправильному узлу ранга i за время O(1).
  • Уменьшение ключа у элемента ранга i соответствует операции инкрементирования i -го разряда счетчика нарушений (естественно, лишь в случае, когда новое значение ключа у изменяемого узла становится меньше значения ключа его родителя).
  • Операции инкрементирования и декрементирования i -го разряда осуществляются за время O(1).

Представление счетчика нарушений. Счетчик нарушений — это расширяющийся массив, элементы которого являются записями из четырех полей

\eq*{
({\rm Value}, {\rm ForwardPointer}, {\rm FirstViolation},
{\rm SecondViolation})
}

со следующей интерпретацией: {\rm CountViolation}[i].{\rm Value} — количество неправильных узлов ранга i в куче, {\rm CountViolation}[i].{\rm ForwardPointer}прямой указатель i -го разряда, {\rm CountViolation}[i].{\rm
FirstViolation} и {\rm CountViolation}[i].{\rm SecondViolation} — указатели на неправильные узлы ранга i.

Заметим, что если значение {\rm CountViolation}[i].{\rm Value} равно единице, то важно лишь значение первого указателя {\rm FirstViolation} и не играет роли значение второго {\rm SecondViolation}. Если {\rm CountViolation}[i].{\rm Value} равно нулю, то неинтересны оба указателя.

Далее ограничимся рассмотрением только наиболее важных операций. Так как счетчик нарушений похож на описанный выше корневой счетчик, акцентируем внимание лишь на различиях. Реализация всех необходимых процедур остается читателю в качестве упражнения.

Инициализация нового звена. Для инициализации нового звена счетчика нарушений необходимо лишь занулить его значение в новом разряде. Делается это только тогда, когда мы вводим в кучу новое дерево ранга {\rm MaxRank} + 1. Это первый момент появления в куче узла ранга {\rm MaxRank} + 1. Для тех нарушений, которые могут возникнуть в узлах ранга меньше либо равного {\rm MaxRank} + 1, соответствующие разряды счетчика нарушений уже инициализированы, а узлов большего ранга в куче пока нет.

Антон Сиротинкин
Антон Сиротинкин

на стр 6, лекции 3, Очевидно "Ck <= модуль(Gk(е))*b(k+1)" (1) - , подскажите что значит "модуль" и почему это очевидно...